#pragma once
/// Copyright 2012, Alex Averian, Yonan Research
/// Released under BSD Licence


#include  "qcontrol.h"
#include "qimagelist.h"


namespace micro
{
namespace gui
{



class QCombo : public QControl
{
public:
// Constructors
    QCombo( HWND h = nullptr )
    {
        Attach( h );
    }

    QCombo & operator =( HWND h )
    {
        Attach( h );
        return *this;
    }

    virtual bool Create( QWindow * parent, int id = 0, UINT style = 0 );



// Attributes
    virtual LPCTSTR GetWndClassName()
    {
        return _T( "COMBOBOX" );
    }

    // for entire combo box
    int GetCount() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETCOUNT, 0, 0L );
    }

    int GetCurSel() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETCURSEL, 0, 0L );
    }
#if( WINVER>=_WIN32_WINNT_WIN7)
    int SetCueBanner( LPCTSTR lParam )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, ( UINT ) CB_SETCUEBANNER, ( WPARAM ) 0, ( LPARAM ) lParam );
    }
#endif
    int SetCurSel( int nSelect )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETCURSEL, nSelect, 0L );
    }

    LCID GetLocale() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( LCID )::SendMessage( _window, CB_GETLOCALE, 0, 0L );
    }

    LCID SetLocale( LCID nNewLocale )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( LCID )::SendMessage( _window, CB_SETLOCALE, ( WPARAM )nNewLocale, 0L );
    }

    int GetTopIndex() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETTOPINDEX, 0, 0L );
    }

    int SetTopIndex( int nIndex )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETTOPINDEX, nIndex, 0L );
    }

    UINT GetHorizontalExtent() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( UINT )::SendMessage( _window, CB_GETHORIZONTALEXTENT, 0, 0L );
    }

    void SetHorizontalExtent( UINT nExtent )
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, CB_SETHORIZONTALEXTENT, nExtent, 0L );
    }

    int GetDroppedWidth() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETDROPPEDWIDTH, 0, 0L );
    }

    int SetDroppedWidth( UINT nWidth )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETDROPPEDWIDTH, nWidth, 0L );
    }


    BOOL GetComboBoxInfo( PCOMBOBOXINFO p ) const
    {
        ASSERT( ::IsWindow( _window ) );
        p->cbSize = sizeof( COMBOBOXINFO );
        return ( BOOL )::GetComboBoxInfo( _window, p );
        //::SendMessage(_window, CB_GETCOMBOBOXINFO, 0, (LPARAM)pComboBoxInfo);

    }

    // for edit control
    DWORD GetEditSel() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( DWORD )::SendMessage( _window, CB_GETEDITSEL, 0, 0L );
    }

    BOOL SetEditSel( int nStartChar, int nEndChar )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_SETEDITSEL, 0, MAKELONG( nStartChar, nEndChar ) );
    }

    // for combobox item
    DWORD_PTR GetItemData( int nIndex ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( DWORD_PTR )::SendMessage( _window, CB_GETITEMDATA, nIndex, 0L );
    }

    int SetItemData( int nIndex, DWORD_PTR dwItemData )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETITEMDATA, nIndex, ( LPARAM )dwItemData );
    }

    void * GetItemDataPtr( int nIndex ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( void * )GetItemData( nIndex );
    }

    int SetItemDataPtr( int nIndex, void * pData )
    {
        ASSERT( ::IsWindow( _window ) );
        return SetItemData( nIndex, ( DWORD_PTR )pData );
    }

    int GetLBText( int nIndex, LPTSTR lpszText ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETLBTEXT, nIndex, ( LPARAM )lpszText );
    }

#ifndef _ATL_NO_COM
    BOOL GetLBTextBSTR( int nIndex, BSTR & bstrText ) const
    {

        ASSERT( ::IsWindow( _window ) );
        ASSERT( bstrText == nullptr );

        int nLen = GetLBTextLen( nIndex );

        if ( nLen == CB_ERR )
            return FALSE;

        QString buff;
        LPTSTR lpstrText = buff.GetBuffer( nLen + 1 );

        if ( lpstrText == nullptr )
            return FALSE;

        if ( GetLBText( nIndex, lpstrText ) == CB_ERR )
            return FALSE;

        bstrText = ::SysAllocString( ( OLECHAR * )( lpstrText ) );
        return ( bstrText != nullptr ) ? TRUE : FALSE;
    }
#endif // !_ATL_NO_COM


    int GetLBText( int nIndex,  QString & strText ) const
    {
        ASSERT( ::IsWindow( _window ) );
        int cchLen = GetLBTextLen( nIndex );

        if ( cchLen == CB_ERR )
            return CB_ERR;

        int nRet = CB_ERR;
        LPTSTR lpstr = new TCHAR[ cchLen + 1];

        if ( lpstr != nullptr )
        {
            nRet = GetLBText( nIndex, lpstr );
            strText = lpstr;

            delete [] lpstr;
        }

        return nRet;
    }


    int GetLBTextLen( int nIndex ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETLBTEXTLEN, nIndex, 0L );
    }

    int GetItemHeight( int nIndex ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETITEMHEIGHT, nIndex, 0L );
    }

    int SetItemHeight( int nIndex, UINT cyItemHeight )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETITEMHEIGHT, nIndex, MAKELONG( cyItemHeight, 0 ) );
    }

    BOOL GetExtendedUI() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_GETEXTENDEDUI, 0, 0L );
    }

    int SetExtendedUI( BOOL bExtended = TRUE )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SETEXTENDEDUI, bExtended, 0L );
    }

    void GetDroppedControlRect( LPRECT lprect ) const
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, CB_GETDROPPEDCONTROLRECT, 0, ( LPARAM )lprect );
    }

    BOOL GetDroppedState() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_GETDROPPEDSTATE, 0, 0L );
    }

#if( WINVER>=_WIN32_WINNT_WIN7)

    int GetMinVisible() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_GETMINVISIBLE, 0, 0L );
    }

    BOOL SetMinVisible( int nMinVisible )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_SETMINVISIBLE, nMinVisible, 0L );
    }

    // Vista only
    BOOL GetCueBannerText( LPWSTR lpwText, int cchText ) const
    {
#ifndef CB_GETCUEBANNER
        const UINT CB_GETCUEBANNER = ( CBM_FIRST + 4 );
#endif
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_GETCUEBANNER, ( WPARAM )lpwText, cchText );
    }

    // Vista only
    BOOL SetCueBannerText( LPCWSTR lpcwText )
    {
#ifndef CB_SETCUEBANNER
        const UINT CB_SETCUEBANNER = ( CBM_FIRST + 3 );
#endif
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_SETCUEBANNER, 0, ( LPARAM )lpcwText );
    }

#endif


// Operations
    int InitStorage( int nItems, UINT nBytes )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_INITSTORAGE, ( WPARAM )nItems, nBytes );
    }



    // for edit control
    BOOL LimitText( int nMaxChars )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CB_LIMITTEXT, nMaxChars, 0L );
    }

    // for drop-down combo boxes
    void ShowDropDown( BOOL bShowIt = TRUE )
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, CB_SHOWDROPDOWN, bShowIt, 0L );
    }

    // manipulating listbox items
    int AddString( LPCTSTR lpszString )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_ADDSTRING, 0, ( LPARAM )lpszString );
    }
    int InsertString( int nIndex, LPCTSTR lpszString )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_INSERTSTRING, nIndex, ( LPARAM )lpszString );
    }
    int DeleteString( UINT nIndex )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_DELETESTRING, nIndex, 0L );
    }
    void DeleteAll()
    {
        ResetContent();
    }
    void ResetContent()
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, CB_RESETCONTENT, 0, 0L );
    }


#ifndef _WIN32_WCE
    int Dir( UINT attr, LPCTSTR lpszWildCard )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_DIR, attr, ( LPARAM )lpszWildCard );
    }
#endif // !_WIN32_WCE

    // selection helpers
    int FindString( int nStartAfter, LPCTSTR lpszString ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_FINDSTRING, nStartAfter, ( LPARAM )lpszString );
    }

    int FindStringExact( int nIndexStart, LPCTSTR lpszFind ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_FINDSTRINGEXACT, nIndexStart, ( LPARAM )lpszFind );
    }

    int SelectString( int nStartAfter, LPCTSTR lpszString )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CB_SELECTSTRING, nStartAfter, ( LPARAM )lpszString );
    }

    // Clipboard operations
    void Clear()
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, WM_CLEAR, 0, 0L );
    }

    void Copy()
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, WM_COPY, 0, 0L );
    }

    void Cut()
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, WM_CUT, 0, 0L );
    }

    void Paste()
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, WM_PASTE, 0, 0L );
    }
};






////////////////////////////////////////////////////////////////////////////////////////////////////////////

class QComboBox: public QControl
{
public:
    QComboBox( HWND h = nullptr );
    virtual ~QComboBox( void );
    virtual bool Create( QWindow * parent, int id = 0, UINT style = 0 );



    QComboBox & operator = ( HWND h )
    {
        Attach( h );
        return *this;
    }


// Attributes
    virtual LPCTSTR GetWndClassName()
    {
        return WC_COMBOBOXEX;
    }

    QImageList GetImageList() const
    {
        ASSERT( ::IsWindow( _window ) );
        return QImageList( ( HIMAGELIST )::SendMessage( _window, CBEM_GETIMAGELIST, 0, 0L ) );
    }

    QImageList SetImageList( HIMAGELIST hImageList )
    {
        ASSERT( ::IsWindow( _window ) );
        return QImageList( ( HIMAGELIST )::SendMessage( _window, CBEM_SETIMAGELIST, 0, ( LPARAM )hImageList ) );
    }


    DWORD GetExtendedStyle() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( DWORD )::SendMessage( _window, CBEM_GETEXTENDEDSTYLE, 0, 0L );
    }

    DWORD SetExtendedStyle( DWORD dwExMask, DWORD dwExStyle )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( DWORD )::SendMessage( _window, CBEM_SETEXTENDEDSTYLE, dwExMask, dwExStyle );
    }

    BOOL GetUnicodeFormat() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CBEM_GETUNICODEFORMAT, 0, 0L );
    }

    BOOL SetUnicodeFormat( BOOL bUnicode = TRUE )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CBEM_SETUNICODEFORMAT, bUnicode, 0L );
    }

#if( WINVER>=_WIN32_WINNT_WIN7)
    void SetWindowTheme( LPCWSTR lpstrTheme )
    {
        ASSERT( ::IsWindow( _window ) );
        ::SendMessage( _window, CBEM_SETWINDOWTHEME, 0, ( LPARAM )lpstrTheme );
    }
#endif


// Operations
    int InsertItem( const COMBOBOXEXITEM * lpcCBItem )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CBEM_INSERTITEM, 0, ( LPARAM )lpcCBItem );
    }

    int InsertItem( UINT nMask, int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage,
                    int iIndent, int iOverlay, LPARAM lParam )
    {
        ASSERT( ::IsWindow( _window ) );
        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = nMask;
        cbex.iItem = nIndex;
        cbex.pszText = ( LPTSTR ) lpszItem;
        cbex.iImage = nImage;
        cbex.iSelectedImage = nSelImage;
        cbex.iIndent = iIndent;
        cbex.iOverlay = iOverlay;
        cbex.lParam = lParam;
        return ( int )::SendMessage( _window, CBEM_INSERTITEM, 0, ( LPARAM )&cbex );
    }

    int InsertItem( int nIndex, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0 )
    {
        ASSERT( ::IsWindow( _window ) );
        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_INDENT | CBEIF_LPARAM;
        cbex.iItem = nIndex;
        cbex.pszText = ( LPTSTR ) lpszItem;
        cbex.iImage = nImage;
        cbex.iSelectedImage = nSelImage;
        cbex.iIndent = iIndent;
        cbex.lParam = lParam;
        return ( int )::SendMessage( _window, CBEM_INSERTITEM, 0, ( LPARAM )&cbex );
    }

    int AddItem( UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, int iOverlay, LPARAM lParam )
    {
        return InsertItem( nMask, -1, lpszItem, nImage, nSelImage, iIndent, iOverlay, lParam );
    }

    int AddItem( LPCTSTR lpszItem, int nImage, int nSelImage, int iIndent, LPARAM lParam = 0 )
    {
        return InsertItem( -1, lpszItem, nImage, nSelImage, iIndent, lParam );
    }

    int DeleteItem( int nIndex )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( int )::SendMessage( _window, CBEM_DELETEITEM, nIndex, 0L );
    }

    BOOL GetItem( PCOMBOBOXEXITEM pCBItem ) const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CBEM_GETITEM, 0, ( LPARAM )pCBItem );
    }

    BOOL SetItem( const COMBOBOXEXITEM * lpcCBItem )
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CBEM_SETITEM, 0, ( LPARAM )lpcCBItem );
    }

    int SetItem( int nIndex, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelImage,
                 int iIndent, int iOverlay, LPARAM lParam )
    {
        ASSERT( ::IsWindow( _window ) );
        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = nMask;
        cbex.iItem = nIndex;
        cbex.pszText = ( LPTSTR ) lpszItem;
        cbex.iImage = nImage;
        cbex.iSelectedImage = nSelImage;
        cbex.iIndent = iIndent;
        cbex.iOverlay = iOverlay;
        cbex.lParam = lParam;
        return ( int )::SendMessage( _window, CBEM_SETITEM, 0, ( LPARAM )&cbex );
    }

    BOOL GetItemText( int nIndex, LPTSTR lpszItem, int nLen ) const
    {
        ASSERT( ::IsWindow( _window ) );
        ASSERT( lpszItem != nullptr );

        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = CBEIF_TEXT;
        cbex.iItem = nIndex;
        cbex.pszText = lpszItem;
        cbex.cchTextMax = nLen;

        return ( BOOL )::SendMessage( _window, CBEM_GETITEM, 0, ( LPARAM )&cbex );
    }

#ifndef _ATL_NO_COM
    BOOL GetItemText( int nIndex, BSTR & bstrText ) const
    {

        ASSERT( ::IsWindow( _window ) );
        ASSERT( bstrText == nullptr );

        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = CBEIF_TEXT;
        cbex.iItem = nIndex;

        LPTSTR lpstrText = nullptr;
        BOOL bRet = FALSE;

        for ( int nLen = 256; ; nLen *= 2 )
        {
            lpstrText = new TCHAR[nLen] ;

            if ( lpstrText == nullptr )
                break;

            lpstrText[0] = 0;
            cbex.pszText = lpstrText;
            cbex.cchTextMax = nLen;
            bRet = ( BOOL )::SendMessage( _window, CBEM_GETITEM, 0, ( LPARAM )&cbex );

            if ( !bRet || ( lstrlen( cbex.pszText ) < nLen - 1 ) )
                break;

            delete [] lpstrText;
            lpstrText = nullptr;
        }

        if ( lpstrText != nullptr )
        {
            if ( bRet )
                bstrText = ::SysAllocString(  ( OLECHAR * ) ( lpstrText ) );

            delete [] lpstrText;
        }

        return ( bstrText != nullptr ) ? TRUE : FALSE;
    }
#endif // !_ATL_NO_COM


    BOOL GetItemText( int nIndex, QString & strText ) const
    {
        ASSERT( ::IsWindow( _window ) );

        COMBOBOXEXITEM cbex = { 0 };
        cbex.mask = CBEIF_TEXT;
        cbex.iItem = nIndex;

        strText.Empty();
        BOOL bRet = FALSE;

        for ( int nLen = 256; ; nLen *= 2 )
        {
            cbex.pszText = strText.GetBuffer( nLen );

            if ( cbex.pszText == nullptr )
            {
                bRet = FALSE;
                break;
            }

            cbex.cchTextMax = nLen;
            bRet = ( BOOL )::SendMessage( _window, CBEM_GETITEM, 0, ( LPARAM )&cbex );

            if ( !bRet || ( lstrlen( cbex.pszText ) < nLen - 1 ) )
                break;
        }

        return bRet;
    }


    BOOL SetItemText( int nIndex, LPCTSTR lpszItem )
    {
        ASSERT( ::IsWindow( _window ) );
        return SetItem( nIndex, CBEIF_TEXT, lpszItem, 0, 0, 0, 0, 0 );
    }

    QCombo GetComboCtrl() const
    {
        ASSERT( ::IsWindow( _window ) );
        return QCombo( ( HWND )::SendMessage( _window, CBEM_GETCOMBOCONTROL, 0, 0L ) );
    }

    QEdit GetEditCtrl() const
    {
        ASSERT( ::IsWindow( _window ) );
        return QEdit( ( HWND )::SendMessage( _window, CBEM_GETEDITCONTROL, 0, 0L ) );
    }

    BOOL HasEditChanged() const
    {
        ASSERT( ::IsWindow( _window ) );
        return ( BOOL )::SendMessage( _window, CBEM_HASEDITCHANGED, 0, 0L );
    }



//// Non-functional
//	int AddString(LPCTSTR /*lpszItem*/)
//	{
//		ASSERT(FALSE);  // Not available in CComboBoxEx; use InsertItem
//		return 0;
//	}
//
//	int InsertString(int /*nIndex*/, LPCTSTR /*lpszString*/)
//	{
//		ASSERT(FALSE);  // Not available in CComboBoxEx; use InsertItem
//		return 0;
//	}
//
//	int Dir(UINT /*attr*/, LPCTSTR /*lpszWildCard*/)
//	{
//		ASSERT(FALSE);  // Not available in CComboBoxEx
//		return 0;
//	}
//
//	int FindString(int /*nStartAfter*/, LPCTSTR /*lpszString*/) const
//	{
//		ASSERT(FALSE);  // Not available in CComboBoxEx; try FindStringExact
//		return 0;
//	}
};




}//namespace

}//micro